package org.tron.common.runtime.vm;

import org.bouncycastle.util.encoders.Hex;
import org.junit.Assert;
import org.junit.Test;
import org.tron.common.runtime.TVMTestResult;
import org.tron.common.runtime.TvmTestUtils;
import org.tron.common.utils.WalletUtil;
import org.tron.core.exception.ContractExeException;
import org.tron.core.exception.ContractValidateException;
import org.tron.core.exception.ReceiptCheckErrException;
import org.tron.core.exception.VMIllegalException;
import org.tron.core.vm.config.ConfigLoader;
import org.tron.core.vm.config.VMConfig;
import org.tron.protos.Protocol.Transaction;

public class IstanbulTest extends VMTestBase {


  /*
       pragma solidity ^0.5.12;
      
       contract IstanbulTest {
         constructor() public payable {}
         function getId() public view returns(uint256){
             uint256 id;
         assembly {
           id := chainid()
         }
         return id;
         }
         function getBalance() public view returns(uint256){
         return address(this).balance;
         }
       }
  */

  /**
   *  SELFBALANCE & CHAINID Test.
   */
  @Test
  public void istanbulSelfBalanceChainIdTest()
      throws ContractExeException, ReceiptCheckErrException, VMIllegalException,
      ContractValidateException {
    ConfigLoader.disable = true;
    VMConfig.initAllowTvmTransferTrc10(1);
    VMConfig.initAllowTvmConstantinople(1);
    VMConfig.initAllowTvmSolidity059(1);
    VMConfig.initAllowTvmIstanbul(1);
    String contractName = "IstanbulTest";
    byte[] address = Hex.decode(OWNER_ADDRESS);
    String abi = "[{\"inputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":"
        + "\"constructor\"},{\"constant\":true,\"inputs\":[],\"name\":\"getBalance\",\"outputs\""
        + ":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":"
        + "false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,"
        + "\"inputs\":[],\"name\":\"getId\",\"outputs\":[{\"internalType\":\"uint256\",\"name"
        + "\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\""
        + "type\":\"function\"}]";

    String factoryCode = "608060405260b9806100126000396000f3fe6080604052348015600f57600080fd5b50"
        + "6004361060325760003560e01c806312065fe01460375780635d1ca631146053575b600080fd5b603d60"
        + "6f565b6040518082815260200191505060405180910390f35b60596077565b6040518082815260200191"
        + "505060405180910390f35b600047905090565b600080469050809150509056fea265627a7a723158205b"
        + "011448e0d2a2dcb83ada305318c9858627a91eac024a1d1cac6ed0efbfbd0164736f6c634300050e0032";
    long value = 123456;
    long fee = 100000000;
    long consumeUserResourcePercent = 0;

    // deploy contract
    Transaction tx = TvmTestUtils.generateDeploySmartContractAndGetTransaction(
        contractName, address, abi, factoryCode, value, fee, consumeUserResourcePercent,
        null);
    byte[] istanbulAddress = WalletUtil.generateContractAddress(tx);
    runtime = TvmTestUtils.processTransactionAndReturnRuntime(tx, rootRepository, null);
    Assert.assertNull(runtime.getRuntimeError());

    // SELFBALANCE Test
    String methodByAddr = "getBalance()";
    TVMTestResult result = TvmTestUtils
        .triggerContractAndReturnTvmTestResult(Hex.decode(OWNER_ADDRESS),
            istanbulAddress, TvmTestUtils.parseAbi(methodByAddr, null),
            0, fee, manager, null);
    Assert.assertNull(result.getRuntime().getRuntimeError());
    Assert.assertEquals(Hex.toHexString(result.getRuntime().getResult().getHReturn()),
        "000000000000000000000000000000000000000000000000000000000001e240");
    //balance == 123456

    //CHAINID Test, if genesis block is changed in config file,
    // this value should be changed as well in this test
    methodByAddr = "getId()";
    result = TvmTestUtils
        .triggerContractAndReturnTvmTestResult(Hex.decode(OWNER_ADDRESS),
            istanbulAddress, TvmTestUtils.parseAbi(methodByAddr, null),
            0, fee, manager, null);
    Assert.assertNull(result.getRuntime().getRuntimeError());
    Assert.assertEquals(Hex.toHexString(result.getRuntime().getResult().getHReturn()),
        "00000000000000007adbf8dc20423f587a5f3f8ea83e2877e2129c5128c12d1e");
    //genesis block hash

  }

  /*
   *
  pragma solidity ^0.5.12;

  contract AltBn128 {
    constructor() public payable {}
    //0x0000000000000000000000000000000000000000000000000000000000000001,
    //0x0000000000000000000000000000000000000000000000000000000000000002
    //0x0000000000000000000000000000000000000000000000000000000000000001,
    //0x0000000000000000000000000000000000000000000000000000000000000002
    function callBn256Add(bytes32 ax, bytes32 ay, bytes32 bx, bytes32 by)
    public returns (bytes32[2] memory result) {
      bytes32[4] memory input;
      input[0] = ax;
      input[1] = ay;
      input[2] = bx;
      input[3] = by;
      assembly {
        let success := call(10000, 0x06, 0, input, 0x80, result, 0x40)
        switch success
        case 0 {
          revert(0,0)
        }

      }
    }
    //0x0000000000000000000000000000000000000000000000000000000000000001,
    //0x0000000000000000000000000000000000000000000000000000000000000002
    //0x0000000000000000000000000000000000000000000000000000000000000002
    function callBn256ScalarMul(bytes32 x, bytes32 y, bytes32 scalar)
    public returns (bytes32[2] memory result) {
      bytes32[3] memory input;
      input[0] = x;
      input[1] = y;
      input[2] = scalar;
      assembly {
        let success := call(gas, 0x07, 0, input, 0x60, result, 0x40)
        switch success
        case 0 {
          revert(0,0)
        }
      }
    }

    function callBn256Pairing(bytes memory input) public returns (bytes32 result) {
      // input is a serialized bytes stream of (a1, b1, a2, b2, ..., ak, bk) from (G_1 x G_2)^k
      uint256 len = input.length;
      require(len % 192 == 0);
      assembly {
        let memPtr := mload(0x40)
        let success := call(gas, 0x08, 0, add(input, 0x20), len, memPtr, 0x20)
        switch success
        case 0 {
          revert(0,0)
        } default {
          result := mload(memPtr)
        }
      }
    }

    function convert(uint256 num) public view returns(bytes32) {
        return bytes32(num);
    }

  }
  */

  @Test
  public void altBn128AddMulEnergyChangeTest()
      throws ContractExeException, ReceiptCheckErrException, VMIllegalException,
      ContractValidateException {
    ConfigLoader.disable = true;
    VMConfig.initAllowTvmTransferTrc10(1);
    VMConfig.initAllowTvmConstantinople(1);
    VMConfig.initAllowTvmSolidity059(1);
    VMConfig.initAllowTvmIstanbul(1);
    String contractName = "Alt_bn128AddMulTest";
    byte[] address = Hex.decode(OWNER_ADDRESS);
    String abi = "[]";

    String factoryCode = "6080604052610475806100136000396000f3fe608060405234801561001057600080fd5"
        + "b506004361061004c5760003560e01c80634849f27914610051578063a3908e1b146100d9578063b2acd50"
        + "91461011b578063ec8b466a146101ea575b600080fd5b61009b6004803603608081101561006757600080f"
        + "d5b81019080803590602001909291908035906020019092919080359060200190929190803590602001909"
        + "29190505050610268565b6040518082600260200280838360005b838110156100c65780820151818401526"
        + "020810190506100ab565b5050505090500191505060405180910390f35b610105600480360360208110156"
        + "100ef57600080fd5b8101908080359060200190929190505050610300565b6040518082815260200191505"
        + "060405180910390f35b6101d46004803603602081101561013157600080fd5b81019080803590602001906"
        + "4010000000081111561014e57600080fd5b82018360208201111561016057600080fd5b803590602001918"
        + "4600183028401116401000000008311171561018257600080fd5b91908080601f016020809104026020016"
        + "040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808"
        + "3019250505050505050919291929050505061030d565b6040518082815260200191505060405180910390f"
        + "35b61022a6004803603606081101561020057600080fd5b810190808035906020019092919080359060200"
        + "1909291908035906020019092919050505061035c565b6040518082600260200280838360005b838110156"
        + "1025557808201518184015260208101905061023a565b5050505090500191505060405180910390f35b610"
        + "2706103da565b6102786103fc565b858160006004811061028657fe5b60200201818152505084816001600"
        + "4811061029d57fe5b60200201818152505083816002600481106102b457fe5b60200201818152505082816"
        + "003600481106102cb57fe5b60200201818152505060408260808360006006612710f180600081146102f05"
        + "76102f5565b600080fd5b505050949350505050565b60008160001b9050919050565b60008082519050600"
        + "060c0828161032057fe5b061461032b57600080fd5b6040516020818360208701600060085af1806000811"
        + "461034e5782519450610353565b600080fd5b50505050919050565b6103646103da565b61036c61041e565"
        + "b848160006003811061037a57fe5b602002018181525050838160016003811061039157fe5b60200201818"
        + "152505082816002600381106103a857fe5b602002018181525050604082606083600060075af1806000811"
        + "46103cb576103d0565b600080fd5b5050509392505050565b6040518060400160405280600290602082028"
        + "038833980820191505090505090565b6040518060800160405280600490602082028038833980820191505"
        + "090505090565b604051806060016040528060039060208202803883398082019150509050509056fea2656"
        + "27a7a723158203249ed01ef0204fa2045e44f86a08ef9179a2002609c299f09c3b3e68721b9e764736f6c6"
        + "3430005110032";

    long value = 0;
    long fee = 100000000;
    long consumeUserResourcePercent = 0;

    // deploy contract
    Transaction tx = TvmTestUtils.generateDeploySmartContractAndGetTransaction(
        contractName, address, abi, factoryCode, value, fee, consumeUserResourcePercent,
        null);
    byte[] istanbulAddress = WalletUtil.generateContractAddress(tx);
    runtime = TvmTestUtils.processTransactionAndReturnRuntime(tx, rootRepository, null);
    Assert.assertNull(runtime.getRuntimeError());

    // bn128 add
    String methodAdd = "callBn256Add(bytes32,bytes32,bytes32,bytes32)";
    TVMTestResult result1 = TvmTestUtils
        .triggerContractAndReturnTvmTestResult(Hex.decode(OWNER_ADDRESS),
            istanbulAddress, TvmTestUtils.parseAbi(methodAdd,
                "0000000000000000000000000000000000000000000000000000000000000001\n"
                + "0000000000000000000000000000000000000000000000000000000000000002\n"
                + "0000000000000000000000000000000000000000000000000000000000000001\n"
                + "0000000000000000000000000000000000000000000000000000000000000002"),
            0, fee, manager, null);
    Assert.assertNull(result1.getRuntime().getRuntimeError());


    //bn128 mul
    String methodMul = "callBn256ScalarMul(bytes32,bytes32,bytes32)";
    TVMTestResult result2 = TvmTestUtils
        .triggerContractAndReturnTvmTestResult(Hex.decode(OWNER_ADDRESS),
            istanbulAddress, TvmTestUtils.parseAbi(methodMul,
                "0000000000000000000000000000000000000000000000000000000000000001\n"
                    + "0000000000000000000000000000000000000000000000000000000000000002\n"
                    + "0000000000000000000000000000000000000000000000000000000000000001\n"),
            0, fee, manager, null);
    Assert.assertNull(result2.getRuntime().getRuntimeError());


    VMConfig.initAllowTvmIstanbul(0);

    // bn128 add
    methodAdd = "callBn256Add(bytes32,bytes32,bytes32,bytes32)";
    TVMTestResult result3 = TvmTestUtils
        .triggerContractAndReturnTvmTestResult(Hex.decode(OWNER_ADDRESS),
            istanbulAddress, TvmTestUtils.parseAbi(methodAdd,
                "0000000000000000000000000000000000000000000000000000000000000001\n"
                    + "0000000000000000000000000000000000000000000000000000000000000002\n"
                    + "0000000000000000000000000000000000000000000000000000000000000001\n"
                    + "0000000000000000000000000000000000000000000000000000000000000002"),
            0, fee, manager, null);
    Assert.assertNull(result3.getRuntime().getRuntimeError());


    //bn128 mul
    methodMul = "callBn256ScalarMul(bytes32,bytes32,bytes32)";
    TVMTestResult result4 = TvmTestUtils
        .triggerContractAndReturnTvmTestResult(Hex.decode(OWNER_ADDRESS),
            istanbulAddress, TvmTestUtils.parseAbi(methodMul,
                "0000000000000000000000000000000000000000000000000000000000000001\n"
                    + "0000000000000000000000000000000000000000000000000000000000000002\n"
                    + "0000000000000000000000000000000000000000000000000000000000000001\n"),
            0, fee, manager, null);
    Assert.assertNull(result4.getRuntime().getRuntimeError());

    long energyAddFuncIstanbul = result1.getRuntime().getResult().getEnergyUsed();
    long energyMulFuncIstanbul = result2.getRuntime().getResult().getEnergyUsed();
    long energyAddFunc = result3.getRuntime().getResult().getEnergyUsed();
    long energyMulFunc = result4.getRuntime().getResult().getEnergyUsed();

    Assert.assertEquals(energyAddFunc - energyAddFuncIstanbul,500 - 150);
    Assert.assertEquals(energyMulFunc - energyMulFuncIstanbul,40000 - 6000);
  }

  @Test
  public void altBn128PairingEnergyChangeTest()
      throws ContractExeException, ReceiptCheckErrException, VMIllegalException,
      ContractValidateException {
    ConfigLoader.disable = true;
    VMConfig.initAllowTvmTransferTrc10(1);
    VMConfig.initAllowTvmConstantinople(1);
    VMConfig.initAllowTvmSolidity059(1);
    VMConfig.initAllowTvmIstanbul(1);
    String contractName = "Alt_bn128AddMulTest";
    byte[] address = Hex.decode(OWNER_ADDRESS);
    String abi = "[]";

    String factoryCode = "608060405234801561001057600080fd5b506117c0806100206000396000f300608060"
        + "405260043610610062576000357c010000000000000000000000000000000000000000000000000000000"
        + "0900463ffffffff16806309e0ab9a146100675780630fd5037b1461009657806315c48a6d146100c55780"
        + "63b9e3117f146100f4575b600080fd5b34801561007357600080fd5b5061007c610123565b60405180821"
        + "5151515815260200191505060405180910390f35b3480156100a257600080fd5b506100ab6102a3565b60"
        + "4051808215151515815260200191505060405180910390f35b3480156100d157600080fd5b506100da610"
        + "301565b604051808215151515815260200191505060405180910390f35b34801561010057600080fd5b50"
        + "6101096107eb565b604051808215151515815260200191505060405180910390f35b6000606061012f611"
        + "1ee565b610137611208565b61013f6111ee565b6101c06040519081016040528061019481526020016112"
        + "d96101949139935060408051908101604052807f18b89d143e40a94525c9e9aaa01317d3a13878fd3f89f"
        + "e4bfb49825366b837b681526020017f0e536811c346ede14c615d4a0da23cb39bdb13b98f54f8726f4cb1"
        + "ce0aef31178152509250604080519081016040528060408051908101604052807f28f3c1a3abbcecf79ce"
        + "9055575c4fc64977ca155d970d0acfacdbd9e955d2cf981526020017f0ca87dc9b1b7f6d50e88661e5d26"
        + "563d711cd303d1cfd4c41a9f570c59fe3bc6815250815260200160408051908101604052807f087026b1d"
        + "11d41c57704d49dcc7f06ca45bd645410bcf176625dc1e3fd67090c81526020017f017f53b7486693313c"
        + "0bcc3b6a2538cc1a56cd56579a7c051779acadb229040e815250815250915061027d84610b9f565b90506"
        + "1029a61028b84610c2b565b610293610cc6565b8385610d90565b94505050505090565b60006060806102"
        + "b0610e9b565b8260008151811015156102bf57fe5b906020019060200201819052506102d4610cc6565b8"
        + "160008151811015156102e357fe5b906020019060200201819052506102fa8282610ebf565b9250505090"
        + "565b60008061030c6111ee565b6060806060610319611208565b610321611208565b610329611208565b6"
        + "103316111ee565b6103396111ee565b6103416111ee565b60608060039c5060408051908101604052807e"
        + "da61a2cb529a2823d137c972abfd1e54adce4068725ef37fb93119bef8eb2a81526020017f2b9fe0ec6b7"
        + "be5374483bc5c452b7855789bb13b8ec6780fe1ab74be7256465c8152509b506101c06040519081016040"
        + "5280610194815260200161146d61019491399a506101c0604051908101604052806101948152602001611"
        + "2d9610194913999506101c060405190810160405280610194815260200161160161019491399850604080"
        + "519081016040528060408051908101604052807f03f390d74c20da7654ea80a8d0e54e5169cb8f9008756"
        + "1795d3dbf52ee2a957681526020017eeaa33de59927082c58f70bd81e8126d4ec7171c0b824512db112de"
        + "3ef6d8ac815250815260200160408051908101604052807f0e80d70dc7c5181494dafa2d8cc2aacd685dc"
        + "ac9b00d9ab505a572b31c92ce6981526020017f2aea4a17e0288acdeb5210cae8ce6255f955bcac208649"
        + "a559f38173c94380578152508152509750604080519081016040528060408051908101604052807f20ca1"
        + "e99e03955175640507ad54e6bdc604cea22e1f3b91353a13b3fc029ca3581526020017f12717a5e3b13d1"
        + "fc98794cb3cf027e3a08c65b5b03fe36bb57731e867b61eba381525081526020016040805190810160405"
        + "2807f01c96adab81a138d554597d4f34d171846cad90068a762f0fa04a353f94953b781526020017f041a"
        + "dbb12b67ad412ed08f06499286f0a3b8440509dec18bb33a3dee005c37bb8152508152509650604080519"
        + "081016040528060408051908101604052807f1ba6ba84c45e2e1877b06258df9ef2a55b7c8e8337680e81"
        + "f63f0f964b09f76881526020017f2443549bf5d49aa38f95e74daf0d5a4a3fa44b9ffa8513cbc25fd7cf4"
        + "ea72626815250815260200160408051908101604052807f2961eccf3f1c2350c636d29eff5b9711247a86"
        + "929d9fadae830bfd4a67ee1b5a81526020017f2c7e195f09253f49f35cc90e76ed8781abf82e147e68aa0"
        + "2013e0cdeb11f5b7d81525081525095506106408b610b9f565b945061064b8a610b9f565b935061065689"
        + "610b9f565b925060018d0160405190808252806020026020018201604052801561069557816020015b610"
        + "68261122f565b81526020019060019003908161067a5790505b50915060018d0160405190808252806020"
        + "02602001820160405280156106d557816020015b6106c2611249565b8152602001906001900390816106b"
        + "a5790505b5090506106e18c610c2b565b8260008151811015156106f057fe5b9060200190602002018190"
        + "52508482600181518110151561070d57fe5b9060200190602002018190525083826002815181101515610"
        + "72a57fe5b906020019060200201819052508282600381518110151561074757fe5b906020019060200201"
        + "8190525061075c610cc6565b81600081518110151561076b57fe5b9060200190602002018190525087816"
        + "00181518110151561078857fe5b90602001906020020181905250868160028151811015156107a557fe5b"
        + "90602001906020020181905250858160038151811015156107c257fe5b906020019060200201819052506"
        + "107d98282610ebf565b9d505050505050505050505050505090565b6000806107f66111ee565b60608061"
        + "0801611208565b610809611208565b6108116111ee565b6108196111ee565b60608060029950604080519"
        + "08101604052807f11a77de18b1d616eee1ba5335d541ab6b0214bddea809091251fb2f157c93d00815260"
        + "20017f0c187d7df8f92f38958e0902b612d5c58874b4380d97e91b6e9c12424de8563081525098506101c"
        + "060405190810160405280610194815260200161146d610194913997506101c06040519081016040528061"
        + "019481526020016112d961019491399650604080519081016040528060408051908101604052807f224e2"
        + "56f32533cdb3d10e255f40c75b971ad71e1974332a145b523cd1fa5382981526020017f2ab94d907ffd43"
        + "b92d2d59cd2dc9f277b1dce25112266aa19db9c4af76a43fee81525081526020016040805190810160405"
        + "2807f24976833c58227908501c809826de16eddc884a91369c74647201ad0b1e7c0c081526020017f1673"
        + "3bd92e16d83797dd6378b9cf1d0d6778d12671a8b50144808fceaad9de428152508152509550604080519"
        + "081016040528060408051908101604052807f1f3aaf10e5e6318ea80a7341bfa157f4daca46e6cc208e27"
        + "ad81b2238f3b181381526020017f18117401001ec3ab7863213a8887af00ea653fb8550e43aad6149b91c"
        + "bcff1b8815250815260200160408051908101604052807f1dd5b5f34e47636f307cfa799ccd2a67f8e872"
        + "72d7e31655f99aa77976bc997081526020017f17649c399e0a257293f4554f1d12107950846cd39e9882c"
        + "1205d8e4d20eb6c8b8152508152509450610a3c88610b9f565b9350610a4787610b9f565b925060018a01"
        + "604051908082528060200260200182016040528015610a8657816020015b610a7361122f565b815260200"
        + "190600190039081610a6b5790505b50915060018a01604051908082528060200260200182016040528015"
        + "610ac657816020015b610ab3611249565b815260200190600190039081610aab5790505b509050610ad28"
        + "9610c2b565b826000815181101515610ae157fe5b90602001906020020181905250838260018151811015"
        + "15610afe57fe5b9060200190602002018190525082826002815181101515610b1b57fe5b9060200190602"
        + "0020181905250610b30610cc6565b816000815181101515610b3f57fe5b90602001906020020181905250"
        + "85816001815181101515610b5c57fe5b9060200190602002018190525084816002815181101515610b795"
        + "7fe5b90602001906020020181905250610b908282610ebf565b9a505050505050505050505090565b610b"
        + "a76111ee565b6000826040518082805190602001908083835b602083101515610bdf57805182526020820"
        + "19150602081019050602083039250610bba565b6001836020036101000a03801982511681845116808217"
        + "85525050505050509050019150506040518091039020600190049050610c23610c1d610e9b565b8261115"
        + "2565b915050919050565b610c336111ee565b60007f30644e72e131a029b85045b68181585d97816a9168"
        + "71ca8d3c208c16d87cfd47905060008360000151148015610c70575060008360200151145b15610c93576"
        + "0408051908101604052806000815260200160008152509150610cc0565b60408051908101604052808460"
        + "0001518152602001828560200151811515610cb757fe5b06830381525091505b50919050565b610cce611"
        + "208565b604080519081016040528060408051908101604052807f198e9393920d483a7260bfb731fb5d25"
        + "f1aa493335a9e71297e485b7aef312c281526020017f1800deef121f1e76426a00665e5c4479674322d4f"
        + "75edadd46debd5cd992f6ed815250815260200160408051908101604052807f090689d0585ff075ec9e99"
        + "ad690c3395bc4b313370b38ef355acdadcd122975b81526020017f12c85ea5db8c6deb4aab71808dcb408"
        + "fe3d1e7690c43d37b4ce6cc0166fa7daa815250815250905090565b600060608060026040519080825280"
        + "60200260200182016040528015610dd057816020015b610dbd61122f565b8152602001906001900390816"
        + "10db55790505b5091506002604051908082528060200260200182016040528015610e0e57816020015b61"
        + "0dfb611249565b815260200190600190039081610df35790505b50905086826000815181101515610e215"
        + "7fe5b9060200190602002018190525084826001815181101515610e3e57fe5b9060200190602002018190"
        + "525085816000815181101515610e5b57fe5b9060200190602002018190525083816001815181101515610"
        + "e7857fe5b90602001906020020181905250610e8f8282610ebf565b92505050949350505050565b610ea3"
        + "6111ee565b6040805190810160405280600181526020016002815250905090565b6000806000606060006"
        + "10ed0611270565b600087518951141515610ee257600080fd5b8851955060068602945084604051908082"
        + "528060200260200182016040528015610f1b5781602001602082028038833980820191505090505b50935"
        + "0600092505b858310156110f8578883815181101515610f3957fe5b906020019060200201516000015184"
        + "60006006860201815181101515610f5b57fe5b90602001906020020181815250508883815181101515610"
        + "f7757fe5b90602001906020020151602001518460016006860201815181101515610f9957fe5b90602001"
        + "906020020181815250508783815181101515610fb557fe5b9060200190602002015160000151600060028"
        + "1101515610fd157fe5b60200201518460026006860201815181101515610fea57fe5b9060200190602002"
        + "018181525050878381518110151561100657fe5b906020019060200201516000015160016002811015156"
        + "1102257fe5b6020020151846003600686020181518110151561103b57fe5b906020019060200201818152"
        + "5050878381518110151561105757fe5b9060200190602002015160200151600060028110151561107357f"
        + "e5b6020020151846004600686020181518110151561108c57fe5b90602001906020020181815250508783"
        + "8151811015156110a857fe5b906020019060200201516020015160016002811015156110c457fe5b60200"
        + "2015184600560068602018151811015156110dd57fe5b9060200190602002018181525050828060010193"
        + "5050610f23565b6020826020870260208701600060086107d05a03f19050806000811461111d5761111f5"
        + "65bfe5b5080151561112c57600080fd5b600082600060018110151561113d57fe5b602002015114159650"
        + "50505050505092915050565b61115a6111ee565b611162611293565b60008460000151826000600381101"
        + "51561117857fe5b602002018181525050846020015182600160038110151561119557fe5b602002018181"
        + "525050838260026003811015156111ae57fe5b602002018181525050606083608084600060076107d05a0"
        + "3f1905080600081146111d7576111d9565bfe5b508015156111e657600080fd5b505092915050565b6040"
        + "80519081016040528060008152602001600081525090565b60806040519081016040528061121c6112b65"
        + "65b81526020016112296112b6565b81525090565b60408051908101604052806000815260200160008152"
        + "5090565b60806040519081016040528061125d6112b6565b815260200161126a6112b6565b81525090565"
        + "b602060405190810160405280600190602082028038833980820191505090505090565b60606040519081"
        + "0160405280600390602082028038833980820191505090505090565b60408051908101604052806002906"
        + "0208202803883398082019150509050509056007b0a2020226f70656e223a207b0a202020202270726963"
        + "65223a2039353931372c0a202020202274696d65223a207b0a20202020202022756e6978223a203134383"
        + "33134323430302c0a2020202020202269736f223a2022323031362d31322d33315430303a30303a30302e"
        + "3030305a220a202020207d0a20207d2c0a202022636c6f7365223a207b0a20202020227072696365223a2"
        + "039363736302c0a202020202274696d65223a207b0a20202020202022756e6978223a2031343833323238"
        + "3830302c0a2020202020202269736f223a2022323031372d30312d30315430303a30303a30302e3030305"
        + "a220a202020207d0a20207d2c0a2020226c6f6f6b7570223a207b0a20202020227072696365223a203936"
        + "3736302c0a20202020226b223a20312c0a202020202274696d65223a207b0a20202020202022756e69782"
        + "23a20313438333232383830302c0a2020202020202269736f223a2022323031372d30312d30315430303a"
        + "30303a30302e3030305a220a202020207d0a20207d0a7d0a6578616d706c652e636f6d2f6170692f317b0"
        + "a2020226f70656e223a207b0a20202020227072696365223a2039353931372c0a202020202274696d6522"
        + "3a207b0a20202020202022756e6978223a20313438333134323430302c0a2020202020202269736f223a2"
        + "022323031362d31322d33315430303a30303a30302e3030305a220a202020207d0a20207d2c0a20202263"
        + "6c6f7365223a207b0a20202020227072696365223a2039363736302c0a202020202274696d65223a207b0"
        + "a20202020202022756e6978223a20313438333232383830302c0a2020202020202269736f223a20223230"
        + "31372d30312d30315430303a30303a30302e3030305a220a202020207d0a20207d2c0a2020226c6f6f6b7"
        + "570223a207b0a20202020227072696365223a2039363736302c0a20202020226b223a20312c0a20202020"
        + "2274696d65223a207b0a20202020202022756e6978223a20313438333232383830302c0a2020202020202"
        + "269736f223a2022323031372d30312d30315430303a30303a30302e3030305a220a202020207d0a20207d"
        + "0a7d0a6578616d706c652e636f6d2f6170692f307b0a2020226f70656e223a207b0a20202020227072696"
        + "365223a2039353931372c0a202020202274696d65223a207b0a20202020202022756e6978223a20313438"
        + "333134323430302c0a2020202020202269736f223a2022323031362d31322d33315430303a30303a30302"
        + "e3030305a220a202020207d0a20207d2c0a202022636c6f7365223a207b0a20202020227072696365223a"
        + "2039363736302c0a202020202274696d65223a207b0a20202020202022756e6978223a203134383332323"
        + "83830302c0a2020202020202269736f223a2022323031372d30312d30315430303a30303a30302e303030"
        + "5a220a202020207d0a20207d2c0a2020226c6f6f6b7570223a207b0a20202020227072696365223a20393"
        + "63736302c0a20202020226b223a20312c0a202020202274696d65223a207b0a20202020202022756e6978"
        + "223a20313438333232383830302c0a2020202020202269736f223a2022323031372d30312d30315430303"
        + "a30303a30302e3030305a220a202020207d0a20207d0a7d0a6578616d706c652e636f6d2f6170692f32a1"
        + "65627a7a72305820b51062a49e0cc84c2e1cf6d19841ccff0280902a2b46a251c52251505ca283380029";

    long value = 0;
    long fee = 1000000000;
    long consumeUserResourcePercent = 0;

    // deploy contract
    Transaction tx = TvmTestUtils.generateDeploySmartContractAndGetTransaction(
        contractName, address, abi, factoryCode, value, fee, consumeUserResourcePercent,
        null);
    byte[] istanbulAddress = WalletUtil.generateContractAddress(tx);
    runtime = TvmTestUtils.processTransactionAndReturnRuntime(tx, rootRepository, null);
    Assert.assertNull(runtime.getRuntimeError());

    // bn128 add
    String methodAdd = "verifyBGLS2()";
    TVMTestResult result1 = TvmTestUtils
        .triggerContractAndReturnTvmTestResult(Hex.decode(OWNER_ADDRESS),
            istanbulAddress, TvmTestUtils.parseAbi(methodAdd,
                null),
            0, fee, manager, null);
    Assert.assertNull(result1.getRuntime().getRuntimeError());

    VMConfig.initAllowTvmIstanbul(0);

    // bn128 add
    methodAdd = "verifyBGLS2()";
    TVMTestResult result2 = TvmTestUtils
        .triggerContractAndReturnTvmTestResult(Hex.decode(OWNER_ADDRESS),
            istanbulAddress, TvmTestUtils.parseAbi(methodAdd,
                null),
            0, fee, manager, null);
    Assert.assertNull(result2.getRuntime().getRuntimeError());
    long energyParingFuncIstanbul = result1.getRuntime().getResult().getEnergyUsed();
    long energyParingFunc = result2.getRuntime().getResult().getEnergyUsed();

    //verifyBGLS2() = 3 * paring + 2 * mul
    Assert.assertEquals(energyParingFunc - energyParingFuncIstanbul,
        (80000L * 3 + 100000) - (34000L * 3 + 45000) + (40000 - 6000) * 2);
  }

  /*
   pragma solidity ^0.4.14;
  contract Alt_bn128PairingEnergyChangeTest {
    struct G1Point {
      uint X;
      uint Y;
    }
    // Encoding of field elements is: X[0] * z + X[1]
    struct G2Point {
      uint[2] X;
      uint[2] Y;
    }


    /// @return the generator of G1
    function P1() internal returns (G1Point) {
    return G1Point(1, 2);
    }

    /// @return the generator of G2
    function P2() internal returns (G2Point) {
    return G2Point(
        [11559732032986387107991004021392285783925812861821192530917403151452391805634,
    10857046999023057135944570762232829481370756359578518086990519993285655852781],

            [4082367875863433681332203403145435568316851327593401208105741076214120093531,
        8495653923123431417604973247489272438418190587263600148770280649306958101930]
        );
    }

    function verifyBLSTest1() public returns (bool) {
      G1Point[] memory a_test1 ;
      G2Point[] memory b_test1 ;
      a_test1[0] = P1();
      b_test1[0] = P2();
      return pairing(a_test1 , b_test1);
    }

    //Example of BLS signature verification
    function verifyBLSTest() returns (bool) {

      bytes memory message = hex"7b0a2020226f70656e223a207b0a20202020227072696365223a2039353931372c
      0a202020202274696d65223a207b0a20202020202022756e6978223a20313438333134323430302c0a20202020202
      02269736f223a2022323031362d31322d33315430303a30303a30302e3030305a220a202020207d0a20207d2c0a20
      2022636c6f7365223a207b0a20202020227072696365223a2039363736302c0a202020202274696d65223a207b0a2
      0202020202022756e6978223a20313438333232383830302c0a2020202020202269736f223a2022323031372d3031
      2d30315430303a30303a30302e3030305a220a202020207d0a20207d2c0a2020226c6f6f6b7570223a207b0a20202
      020227072696365223a2039363736302c0a20202020226b223a20312c0a202020202274696d65223a207b0a202020
      20202022756e6978223a20313438333232383830302c0a2020202020202269736f223a2022323031372d30312d303
      15430303a30303a30302e3030305a220a202020207d0a20207d0a7d0a6578616d706c652e636f6d2f6170692f31";

      G1Point memory signature = G1Point(1118169234584895766207429087813834422708559713498101904073
      5323471731897153462, 647974644704657036043571424927277608278793214621176425134779866844738192
      6167);

      G2Point memory v = G2Point(
          [18523194229674161632574346342370534213928970227736813349975332190798837787897,
          5725452645840548248571879966249653216818629536104756116202892528545334967238],
            [3816656720215352836236372430537606984911914992659540439626020770732736710924,
             677280212051826798882467475639465784259337739185938192379192340908771705870]
        );

      G1Point memory h = hashToG1(message);

      return pairing2(negate(signature), P2(), h, v);
    }

    //Example of BGLS signature verification with 2 signers
    //Note that the messages differ in their last character.
    function verifyBGLS2() returns (bool) {

      uint numberOfSigners = 2;

      G1Point memory signature =
      G1Point(7985250684665362734034207174567341000146996823387166378141631317099216977152,
       5471024627060516972461571110176333017668072838695251726406965080926450112048);

      bytes memory message0 =
      hex"7b0a2020226f70656e223a207b0a20202020227072696365223a2039353931372c0a202020202274696d65
      223a207b0a20202020202022756e6978223a20313438333134323430302c0a2020202020202269736f223a2022
      323031362d31322d33315430303a30303a30302e3030305a220a202020207d0a20207d2c0a202022636c6f7365
      223a207b0a20202020227072696365223a2039363736302c0a202020202274696d65223a207b0a202020202020
      22756e6978223a20313438333232383830302c0a2020202020202269736f223a2022323031372d30312d303154
      30303a30303a30302e3030305a220a202020207d0a20207d2c0a2020226c6f6f6b7570223a207b0a2020202022
      7072696365223a2039363736302c0a20202020226b223a20312c0a202020202274696d65223a207b0a20202020
      202022756e6978223a20313438333232383830302c0a2020202020202269736f223a2022323031372d30312d30
      315430303a30303a30302e3030305a220a202020207d0a20207d0a7d0a6578616d706c652e636f6d2f6170692f
      30";
      bytes memory message1 = hex"7b0a2020226f70656e223a207b0a20202020227072696365223a2039353931
      372c0a202020202274696d65223a207b0a20202020202022756e6978223a20313438333134323430302c0a2020
      202020202269736f223a2022323031362d31322d33315430303a30303a30302e3030305a220a202020207d0a20
      207d2c0a202022636c6f7365223a207b0a20202020227072696365223a2039363736302c0a202020202274696d
      65223a207b0a20202020202022756e6978223a20313438333232383830302c0a2020202020202269736f223a20
      22323031372d30312d30315430303a30303a30302e3030305a220a202020207d0a20207d2c0a2020226c6f6f6b
      7570223a207b0a20202020227072696365223a2039363736302c0a20202020226b223a20312c0a202020202274
      696d65223a207b0a20202020202022756e6978223a20313438333232383830302c0a2020202020202269736f22
      3a2022323031372d30312d30315430303a30303a30302e3030305a220a202020207d0a20207d0a7d0a6578616d
      706c652e636f6d2f6170692f31";

      G2Point memory v0 = G2Point(
          [15516709285352539082439213720585739724329002971882390582209636960597958801449,
          19324541677661060388134143597417835654030498723817274130329567224531700170734],
            [16550775633156536193089672538964908973667410921848053632462693002610771214528,
            10154483139478025296468271477739414260393126999813603835827647034319242387010]
        );

      G2Point memory v1 = G2Point(
          [14125383697019450293340447180826714775062600193406387386692146468060627933203,
          10886345395648455940547500614900453787797209052692168129177801883734751834552],
            [13494666809312056575532152175382485778895768300692817869062640713829304801648,
            10580958449683540742032499469496205826101096579572266360455646078388895706251]
        );

      G1Point memory h0 = hashToG1(message0);
      G1Point memory h1 = hashToG1(message1);

      G1Point[] memory a = new G1Point[](numberOfSigners + 1);
      G2Point[] memory b = new G2Point[](numberOfSigners + 1);
      a[0] = negate(signature);
      a[1] = h0;
      a[2] = h1;
      b[0] = P2();
      b[1] = v0;
      b[2] = v1;

      return pairing(a, b);
    }

    //Example of BGLS signature verification with 3 signers
    //Note that the messages differ in their last character.
    function verifyBGLS3() returns (bool) {

      uint numberOfSigners = 3;

      G1Point memory signature =
      G1Point(385846518441062319503502284295243290270560187383398932887791670182362540842,
      19731933537428695151702009864745685458233056709189425720845387511061953267292);

      bytes memory message0 =
      hex"7b0a2020226f70656e223a207b0a20202020227072696365223a2039353931372c0a202020202274696d65
      223a207b0a20202020202022756e6978223a20313438333134323430302c0a2020202020202269736f223a2022
      323031362d31322d33315430303a30303a30302e3030305a220a202020207d0a20207d2c0a202022636c6f7365
      223a207b0a20202020227072696365223a2039363736302c0a202020202274696d65223a207b0a202020202020
      22756e6978223a20313438333232383830302c0a2020202020202269736f223a2022323031372d30312d303154
      30303a30303a30302e3030305a220a202020207d0a20207d2c0a2020226c6f6f6b7570223a207b0a2020202022
      7072696365223a2039363736302c0a20202020226b223a20312c0a202020202274696d65223a207b0a20202020
      202022756e6978223a20313438333232383830302c0a2020202020202269736f223a2022323031372d30312d30
      315430303a30303a30302e3030305a220a202020207d0a20207d0a7d0a6578616d706c652e636f6d2f6170692f
      30";
      bytes memory message1 =
      hex"7b0a2020226f70656e223a207b0a20202020227072696365223a2039353931372c0a202020202274696d65
      223a207b0a20202020202022756e6978223a20313438333134323430302c0a2020202020202269736f223a2022
      323031362d31322d33315430303a30303a30302e3030305a220a202020207d0a20207d2c0a202022636c6f7365
      223a207b0a20202020227072696365223a2039363736302c0a202020202274696d65223a207b0a202020202020
      22756e6978223a20313438333232383830302c0a2020202020202269736f223a2022323031372d30312d303154
      30303a30303a30302e3030305a220a202020207d0a20207d2c0a2020226c6f6f6b7570223a207b0a2020202022
      7072696365223a2039363736302c0a20202020226b223a20312c0a202020202274696d65223a207b0a20202020
      202022756e6978223a20313438333232383830302c0a2020202020202269736f223a2022323031372d30312d30
      315430303a30303a30302e3030305a220a202020207d0a20207d0a7d0a6578616d706c652e636f6d2f6170692f
      31";
      bytes memory message2 =
      hex"7b0a2020226f70656e223a207b0a20202020227072696365223a2039353931372c0a202020202274696d65
      223a207b0a20202020202022756e6978223a20313438333134323430302c0a2020202020202269736f223a2022
      323031362d31322d33315430303a30303a30302e3030305a220a202020207d0a20207d2c0a202022636c6f7365
      223a207b0a20202020227072696365223a2039363736302c0a202020202274696d65223a207b0a202020202020
      22756e6978223a20313438333232383830302c0a2020202020202269736f223a2022323031372d30312d303154
      30303a30303a30302e3030305a220a202020207d0a20207d2c0a2020226c6f6f6b7570223a207b0a2020202022
      7072696365223a2039363736302c0a20202020226b223a20312c0a202020202274696d65223a207b0a20202020
      202022756e6978223a20313438333232383830302c0a2020202020202269736f223a2022323031372d30312d30
      315430303a30303a30302e3030305a220a202020207d0a20207d0a7d0a6578616d706c652e636f6d2f6170692f
      32";

      G2Point memory v0 = G2Point(
          [1787282038370667094324364195810339512415273589223814213215040505578200405366,
          414568866548933554513940840943382696902163788831396286279770126458218272940],
            [6560020551439455112781785895092032589010633560844445112872109862153018855017,
            19411093226570397520343120724285433000937737461010544490862811136406407315543]
        );

      G2Point memory v1 = G2Point(
          [14831125462625540363404323739936082597729714855858291605999144010730542058037,
          8342129546329626371616639780890580451066604883761980695690870205390518348707],
            [808186590373043742842665711030588185456231663895663328011864547134240543671,
            1856705676948889458735296604372981546875220644939188415241687241562401814459]
        );

      G2Point memory v2 = G2Point(
          [12507030828714819990408995725310388936101611986473926829733453468215798265704,
          16402225253711577242710704509153100189802817297679524801952098990526969620006],
            [18717845356690477533392378472300056893077745517009561191866660997312973511514,
            20124563173642533900823905467925868861151292863229012000403558815142682516349]
        );

      G1Point memory h0 = hashToG1(message0);
      G1Point memory h1 = hashToG1(message1);
      G1Point memory h2 = hashToG1(message2);

      G1Point[] memory a = new G1Point[](numberOfSigners + 1);
      G2Point[] memory b = new G2Point[](numberOfSigners + 1);
      a[0] = negate(signature);
      a[1] = h0;
      a[2] = h1;
      a[3] = h2;
      b[0] = P2();
      b[1] = v0;
      b[2] = v1;
      b[3] = v2;

      return pairing(a, b);
    }

    /// @return the result of computing the pairing check
    /// e(p1[0], p2[0]) *  .... * e(p1[n], p2[n]) == 1
    /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should
    /// return true.
    function pairing(G1Point[] p1, G2Point[] p2) internal returns (bool) {
        require(p1.length == p2.length);
    uint elements = p1.length;
    uint inputSize = elements * 6;
    uint[] memory input = new uint[](inputSize);

    for (uint i = 0; i < elements; i++)
    {
      input[i * 6 + 0] = p1[i].X;
      input[i * 6 + 1] = p1[i].Y;
      input[i * 6 + 2] = p2[i].X[0];
      input[i * 6 + 3] = p2[i].X[1];
      input[i * 6 + 4] = p2[i].Y[0];
      input[i * 6 + 5] = p2[i].Y[1];
    }

    uint[1] memory out;
    bool success;

    assembly {
      success := call(sub(gas, 2000), 8, 0, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
      // Use "invalid" to make gas estimation work
      switch success case 0 {invalid}
    }
    require(success);
    return out[0] != 0;
    }

    /// Convenience method for a pairing check for two pairs.
    function pairing2(G1Point a1, G2Point a2, G1Point b1, G2Point b2) internal returns (bool) {
        G1Point[] memory p1 = new G1Point[](2);
    G2Point[] memory p2 = new G2Point[](2);
    p1[0] = a1;
    p1[1] = b1;
    p2[0] = a2;
    p2[1] = b2;
    return pairing(p1, p2);
    }

    function hashToG1(bytes message) internal returns (G1Point) {
        uint256 h = uint256(keccak256(message));
    return mul(P1(), h);
    }

    function modPow(uint256 base, uint256 exponent, uint256 modulus) internal returns (uint256) {
        uint256[6] memory input = [32, 32, 32, base, exponent, modulus];
    uint256[1] memory result;
    assembly {
      if iszero(call(not(0), 0x05, 0, input, 0xc0, result, 0x20)) {
        revert(0, 0)
      }
    }
    return result[0];
    }

    /// @return the negation of p, i.e. p.add(p.negate()) should be zero.
    function negate(G1Point p) internal returns (G1Point) {
        // The prime q in the base field F_q for G1
        uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
    if (p.X == 0 && p.Y == 0)
      return G1Point(0, 0);
    return G1Point(p.X, q - (p.Y % q));
    }

    /// @return the sum of two points of G1
    function add(G1Point p1, G1Point p2) internal returns (G1Point r) {
      uint[4] memory input;
      input[0] = p1.X;
      input[1] = p1.Y;
      input[2] = p2.X;
      input[3] = p2.Y;
      bool success;
      assembly {
        success := call(sub(gas, 2000), 6, 0, input, 0xc0, r, 0x60)
        // Use "invalid" to make gas estimation work
        switch success case 0 {invalid}
      }
      require(success);
    }
    /// @return the product of a point on G1 and a scalar, i.e.
    /// p == p.mul(1) and p.add(p) == p.mul(2) for all points p.
    function mul(G1Point p, uint s) internal returns (G1Point r) {
      uint[3] memory input;
      input[0] = p.X;
      input[1] = p.Y;
      input[2] = s;
      bool success;
      assembly {
        success := call(sub(gas, 2000), 7, 0, input, 0x80, r, 0x60)
        // Use "invalid" to make gas estimation work
        switch success case 0 {invalid}
      }
      require(success);
    }

  }
   */
}
